#include "imagem.h"
#include <QPainter>

Imagem::Imagem() {
    largura = 0;
    altura = 0;
    deslocamento = 0;
}

int Imagem::getLargura() {
    return largura;
}

int Imagem::getAltura() {
    return altura;
}

int Imagem::getDeslocamento() {
    return deslocamento;
}

QColor Imagem::getCor(int x, int y) {
    return pixels[x][y];
}

void Imagem::setLargura(unsigned int l) {
    largura = l;
}

void Imagem::setAltura(unsigned int a) {
    altura = a;
}

void Imagem::setDeslocamento(unsigned int d) {
    deslocamento = d;
}

void Imagem::setCor(int x, int y, QColor c) {
    pixels[x][y] = c;
}

QByteArray Imagem::getBytes() {

    QByteArray b;
                        // monta o cabecalho

    b.insert(0, "BM");
    b.insert(2, (char*) &c.BfSize ,         4);
    b.insert(6, (char*) &c.BfReser1 ,       2);
    b.insert(8, (char*) &c.BfReser2 ,       2);
    b.insert(10, (char*) &c.BfOffSetBits ,  4);
    b.insert(14, (char*) &c.BiSize ,        4);
    b.insert(18, (char*) &c.BiWidth ,       4);
    b.insert(22, (char*) &c.BiHeight ,      4);
    b.insert(26, (char*) &c.BiPlanes ,      2);
    b.insert(28, (char*) &c.BiBitCount ,    2);
    b.insert(30, (char*) &c.BiCompress ,    4);
    b.insert(34, (char*) &c.BiSizeImag ,    4);
    b.insert(38, (char*) &c.BiXPPMeter ,    4);
    b.insert(42, (char*) &c.BiYPPMeter ,    4);
    b.insert(46, (char*) &c.BiClrUsed ,     4);
    b.insert(50, (char*) &c.BiClrImpor ,    4);

    char lixo = 4-(largura%4);      // calcula o lixo
    int deslocamento = 54;

    for (int a = (c.BiHeight-1); a >=0; a--) {
        for (int l = 0; l < c.BiWidth; l++) {
            QColor p = pixels[l][a];
            char azul = p.blue();
            char verde = p.green();
            char vermelho = p.red();

            b.insert(deslocamento,   (char*) &azul,     1);
            b.insert(deslocamento+1, (char*) &verde,    1);
            b.insert(deslocamento+2, (char*) &vermelho, 1);
            deslocamento +=3;
        }
        for (int i = 0; i < lixo; i++) {        // adiciona o lixo no final da linha
            char zero = 0;
            b.insert(deslocamento, (char*) &zero, 1);
            deslocamento++;
        }
    }
    qDebug () << "montagem ok";

    return b;
}

bool Imagem::AND(QString dir) {

    qDebug() << "AND";
    QFile file (dir);
    if ( !file.open( QIODevice::ReadOnly ) ) {
        qDebug () << "Imagem::ler: erro ao abrir o arquivo";
        return false;
    }

    QByteArray b = file.readAll();          // carrega imagem pra memoria

    QColor** matriz;

    QString tipo = b.mid(0, 2);
    if ( tipo == "BM"  ) {      // verifica o tipo do arquivo
        tipo = b.mid(28, 2);
        if (tipo == "") {      // 24

            int deslocamento = ( *( (unsigned int  *) b.mid( 10,   4).data() ) );

            int largura(  *( (unsigned int  *) b.mid( 18,   4).data() ));
            int altura( *( (unsigned int  *) b.mid( 22,   4).data() ));

            if ((largura != this->largura) || (altura != this->altura)) {
                qDebug() << "Tamanhos incompativeis";
                return false;
            }

            matriz = new QColor*[altura];           // aloca uma matriz dinamica altura*largura
            for (int i = 0; i < altura; ++i)
                matriz[i] = new QColor[largura];

            int lixo = 0;               // usado para calcular os NULLs do final das linhas que nao sao multiplas de 4
            int rodada = 0;             // contem a quantidade de pixels das linhas ja percorridas

            for (int a = (altura-1); a >=0 ; --a) {
                for (int l = 0; l < largura ; ++l) {
                    int local = (rodada*3 + deslocamento + lixo + l*3);     // local contem a posicao a pegar a proxima cor
                    unsigned char c = *( (unsigned char  *) b.mid( local  ,  1).data() );
                    QColor cor;
                    cor.setBlue((int) c);
                    c = *( (unsigned char  *) b.mid( local+1,  1).data() );
                    cor.setGreen((int) c);
                    c = *( (unsigned char  *) b.mid( local+2,  1).data() );
                    cor.setRed((int) c);

                    matriz[a][l] = cor;         // coloca a cor lida na matriz

                }
                rodada += largura;
                lixo += largura%4;
            }
        }
    }

    for (int i = 0; i < altura;i++) {
        for (int j = 0; j < largura; j++) {
            QColor c1 = this->pixels[i][j];
            QColor c2 = matriz[i][j];
            int cor1 = 0;
            int cor2 = 0;

            cor1 = (((c1.red()   & 0xFF))
                   |((c1.green() & 0xFF) << 8)
                   |((c1.blue()  & 0xFF) << 16));

            cor2 = (((c2.red()   & 0xFF))
                   |((c2.green() & 0xFF) << 8)
                   |((c2.blue()  & 0xFF) << 16));



            int corFinal = (cor1 & cor2);
            QColor cFinal;
            cFinal.setRed   (corFinal       & 0xFF);
            cFinal.setGreen (corFinal >> 8  & 0xFF);
            cFinal.setBlue  (corFinal >> 16 & 0xFF);

            pixels[i][j] = cFinal;
        }
    }
    updateMapa();
    return true;
}

bool Imagem::OR(QString dir) {

    qDebug() << "OR";
    QFile file (dir);
    if ( !file.open( QIODevice::ReadOnly ) ) {
        qDebug () << "Imagem::ler: erro ao abrir o arquivo";
        return false;
    }

    QByteArray b = file.readAll();          // carrega imagem pra memoria

    QColor** matriz;

    QString tipo = b.mid(0, 2);
    if ( tipo == "BM"  ) {      // verifica o tipo do arquivo
        tipo = b.mid(28, 2);
        if (tipo == "") {      // 24

            int deslocamento = ( *( (unsigned int  *) b.mid( 10,   4).data() ) );

            int l( *( (unsigned int  *) b.mid( 18,   4).data() ));
            int a( *( (unsigned int  *) b.mid( 22,   4).data() ));

            if (l != getLargura() || a != getAltura()) {
                qDebug() << "Tamanhos incompativeis";
                return false;
            }

            matriz = new QColor*[a];           // aloca uma matriz dinamica altura*largura
            for (int i = 0; i < a; ++i)
                matriz[i] = new QColor[l];

            int lixo = 0;               // usado para calcular os NULLs do final das linhas que nao sao multiplas de 4
            int rodada = 0;             // contem a quantidade de pixels das linhas ja percorridas

            for (int a = (a-1); a >=0 ; --a) {
                for (int l = 0; l < l ; ++l) {
                    int local = (rodada*3 + deslocamento + lixo + l*3);     // local contem a posicao a pegar a proxima cor
                    unsigned char c = *( (unsigned char  *) b.mid( local  ,  1).data() );
                    QColor cor;
                    cor.setBlue((int) c);
                    c = *( (unsigned char  *) b.mid( local+1,  1).data() );
                    cor.setGreen((int) c);
                    c = *( (unsigned char  *) b.mid( local+2,  1).data() );
                    cor.setRed((int) c);

                    matriz[a][l] = cor;         // coloca a cor lida na matriz

                }
                rodada += l;
                lixo += l%4;
            }
        }

    }

    for (int i = 0; i < altura;i++) {
        for (int j = 0; j < largura; j++) {
            QColor c1 = this->pixels[i][j];
            QColor c2 = matriz[i][j];
            int cor1 = 0;
            int cor2 = 0;

            cor1 = (((c1.red()   & 0xFF))
                   |((c1.green() & 0xFF) << 8)
                   |((c1.blue()  & 0xFF) << 16));

            cor2 = (((c2.red()   & 0xFF))
                   |((c2.green() & 0xFF) << 8)
                   |((c2.blue()  & 0xFF) << 16));



            int corFinal = (cor1 | cor2);
            QColor cFinal;
            cFinal.setRed   (corFinal       & 0xFF);
            cFinal.setGreen (corFinal >> 8  & 0xFF);
            cFinal.setBlue  (corFinal >> 16 & 0xFF);

            pixels[i][j] = cFinal;
        }
    }
    updateMapa();
    return true;
}



bool Imagem::ler(QString diretorio, QString* sobre) {

    QFile file (diretorio);
    if ( !file.open( QIODevice::ReadOnly ) ) {
        qDebug () << "Imagem::ler: erro ao abrir o arquivo";
        return false;
    }

    QByteArray b = file.readAll();          // carrega imagem pra memoria

    QString tipo = b.mid(0, 2);
    if ( tipo == "BM"  ) {      // verifica o tipo do arquivo

        // seta o Cabecalho e o Sobre
        c.BfType =       *((unsigned int *) b.mid(0,2).data());
        sobre->append("BfType: " + b.mid(0,2) + "\n");
        c.BfSize =       *((unsigned int *) b.mid(2,4).data());
        sobre->append("BfSize: " + QString::number(c.BfSize) + "\n");
        c.BfReser1 =     *((unsigned int *) b.mid(6,2).data());
        sobre->append("BfReser1: " + QString::number(c.BfReser1) + "\n");
        c.BfReser2 =     *((unsigned int *) b.mid(8,2).data());
        sobre->append("BfReser2: " + QString::number(c.BfReser2) + "\n");
        c.BfOffSetBits = *((unsigned int *) b.mid(10,4).data());
        sobre->append("BfOffSetBits: " + QString::number(c.BfOffSetBits) + "\n\n");

        c.BiSize =     *((unsigned int *) b.mid(14,4).data());
        sobre->append("BiSize: " + QString::number(c.BiSize) + "\n");
        c.BiWidth =    *((unsigned int *) b.mid(18,4).data());
        sobre->append("BiWidth: " + QString::number(c.BiWidth) + "\n");
        c.BiHeight =   *((unsigned int *) b.mid(22,4).data());
        sobre->append("BiHeigt: " + QString::number(c.BiHeight) + "\n");
        c.BiPlanes =   *((unsigned int *) b.mid(26,2).data());
        sobre->append("BiPlanes: " + QString::number(c.BiPlanes) + "\n");
        c.BiBitCount = *((unsigned int *) b.mid(28,2).data());
        sobre->append("BiBitCount: " + QString::number(c.BiBitCount) + "\n");
        c.BiCompress = *((unsigned int *) b.mid(30,4).data());
        sobre->append("BiCompress: " + QString::number(c.BiCompress) + "\n");
        c.BiSizeImag = *((unsigned int *) b.mid(34,4).data());
        sobre->append("BiSizeImag: " + QString::number(c.BiSizeImag) + "\n");
        c.BiXPPMeter = *((unsigned int *) b.mid(38,4).data());
        sobre->append("BiXPPMeter: " + QString::number(c.BiXPPMeter) + "\n");
        c.BiYPPMeter = *((unsigned int *) b.mid(42,4).data());
        sobre->append("BiYPPMeter: " + QString::number(c.BiYPPMeter) + "\n");
        c.BiClrUsed =  *((unsigned int *) b.mid(46,4).data());
        sobre->append("BiClrUsed: " + QString::number(c.BiClrUsed) + "\n");
        c.BiClrImpor = *((unsigned int *) b.mid(50,4).data());
        sobre->append("BiClrImpor: " + QString::number(c.BiClrImpor) + "\n\n");

        int blue = 0;
        int green = 0;
        int red = 0;

        tipo = b.mid(28, 2);
        if (tipo == "") {      // 24
            qDebug() << "Imagem BMP True Color ";

            setDeslocamento( *( (unsigned int  *) b.mid( 10,   4).data() ) );
            qDebug() << "Deslocamento" << getDeslocamento();

            setLargura(  *( (unsigned int  *) b.mid( 18,   4).data() ));
            qDebug() << "Largura" << getLargura();
            setAltura( *( (unsigned int  *) b.mid( 22,   4).data() ));
            qDebug() << "Altura: " << getAltura();


            pixels = new QColor*[altura];           // aloca uma matriz dinamica altura*largura
            for (int i = 0; i < altura; ++i)
                pixels[i] = new QColor[largura];

            int lixo = 0;               // usado para calcular os NULLs do final das linhas que nao sao multiplas de 4
            int rodada = 0;             // contem a quantidade de pixels das linhas ja percorridas

            for (int a = (altura-1); a >=0 ; --a) {
                for (int l = 0; l < largura ; ++l) {
                    int local = (rodada*3 + deslocamento + lixo + l*3);     // local contem a posicao a pegar a proxima cor
                    unsigned char c = *( (unsigned char  *) b.mid( local  ,  1).data() );
                    QColor cor;
                    cor.setBlue((int) c);
                    blue += c;
                    c = *( (unsigned char  *) b.mid( local+1,  1).data() );
                    cor.setGreen((int) c);
                    green += c;
                    c = *( (unsigned char  *) b.mid( local+2,  1).data() );
                    cor.setRed((int) c);
                    red += c;

                    pixels[a][l] = cor;         // coloca a cor lida na matriz

                }
                rodada += largura;
                lixo += largura%4;
            }

            int totPixels = c.BiWidth * c.BiHeight;

            sobre->append("Média Azul: " + QString::number(blue/totPixels) + "\n");
            sobre->append("Média Verde: " + QString::number(green/totPixels) + "\n");
            sobre->append("Média Vermelho: " + QString::number(red/totPixels) + "\n");

            updateMapa();
            return true;
        }
        if (tipo == "") {          // 8
            qDebug() << "Imagem BMP 256 cores";

            setDeslocamento( *( (unsigned int  *) b.mid( 10,   4).data() ) );
            qDebug() << "Deslocamento" << getDeslocamento();

            setAltura( *( (unsigned int  *) b.mid( 22,   4).data() ));
            qDebug() << "Altura: " << getAltura();
            setLargura(  *( (unsigned int  *) b.mid( 18,   4).data() ));
            qDebug() << "Largura" << getLargura();

            QColor paleta [256];

            sobre->append("Paleta\nBlue\tGreen\tRed\n");
            for (int i = 0; i < 256 ; ++i) {
                int local = (i*4 + 54);     // local contem a posicao a pegar a proxima cor
                //qDebug() << "Local[" << i << "]:" << local;
                unsigned char c = *( (unsigned char  *) b.mid( local  ,  1).data() );
                QColor cor;
                cor.setBlue((int) c);
                sobre->append(QString::number(i) + ": " + QString::number(c) + "\t");
                c = *( (unsigned char  *) b.mid( local+1,  1).data() );
                cor.setGreen((int) c);
                sobre->append(QString::number(c) + "\t");
                c = *( (unsigned char  *) b.mid( local+2,  1).data() );
                cor.setRed((int) c);
                sobre->append(QString::number(c) + "\n");

                //qDebug() << "Red: " << cor.red();
                //qDebug() << "Green: " << cor.green();
                //qDebug() << "Blue: " << cor.blue();
                //qDebug();

                paleta[i] = cor;         // coloca a cor lida na paleta
            }

            pixels = new QColor*[altura];           // aloca uma matriz dinamica altura*largura
            for (int i = 0; i < altura; ++i)
                pixels[i] = new QColor[largura];

            int lixo = 0;
            int rodada = 0;
            for (int a = (altura-1); a >=0 ; --a) {
                for (int l = 0; l < largura ; ++l) {
                    int local = ( rodada + l + deslocamento + lixo);     // local contem a posicao a pegar o proximo indice
                    unsigned char c = *( (unsigned char  *) b.mid( local  ,  1).data() );

                    pixels[a][l] = paleta[(int)c];         // coloca a cor lida na matriz

                }
                lixo += (largura*3)%4;
                rodada += largura;
            }

            updateMapa();
            return true;
        }
    }

    qDebug() << "Arquivo Invalido";
    return false;
}

QRectF Imagem::boundingRect() const {
    return QRectF (0,0,largura, altura);            //  seta tamanho da imagem na visao
}

void Imagem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
    painter->drawPixmap(0,0,mapa);      // coloca a imagem no ponto (0,0)
}

void Imagem::updateMapa() {             // para evitar de a imagem ficar redesenhando a todo o momento
    mapa = QPixmap(largura, altura);
    QPainter painter (&mapa);

    for (int i = 0; i < altura; ++i)
        for (int j = 0; j < largura; ++j) {
            painter.setPen(pixels[i][j]);   //  pega a cor do pixel a ser pintado na tela
            painter.drawPoint(j,i);         //  desenha o pixel na posicao desejada
        }
}
